import base64
import pickle

from django.shortcuts import render

# Create your views here.
from django_redis import get_redis_connection
from rest_framework.response import Response

from goods.models import SKU
from users.serializers import CartDeleteSerializer

"""

0.如何区分登录用户和未登录用户
    request.user
    判断 request.user的状态
1.
登录用户保存在redis中
未登录用户保存到cookie中

2.保存的数据 都是3个: sku_id(商品id), count(商品个数),selected(选中状态)

3.  数据保存到redis中 该如何选择数据的结构

    redis是保存在内存中的,我们的内存不能随意浪费


String      key:value
Hash        hash_key:   key:value
            cart_1      sku_id:count


                        1:5



                        2:10



                        3:100


记录哪个商品选中了:  1,2

因为 hash中的( sku_id:count ) key ,都包含了商品的id
 我们单独去记录 选中商品的状态, 如果 hash中的key 没有在 记录的状态中,我们认为该商品没有选中


Set         set_key:    value4,value2,value10
    选中集合
             cart_selected_1   {1,2}







4. cookie的处理
    {
       sku_id:{count:xxx,selected:xxx},

       1:{count:5,selected:True},
       3:{count:50,selected:False},
    }

    pickle
    base64

    # 我们要对数据进行 base64的处理,base64只能对二进制进行处理
    # 所以我们需要先将 数据(dict)转换为二进制


    1G = 1024M
    1M = 1024KB
    1KB = 1024B
    1B = 8bytes

    1 bytes 0 / 1



    0100 0001           A



    0100 0001   0100 0001       0100 0001
        A           A               A

    010000      010100          000101          000001

        x        y                   z               l

"""


"""
用户在详情页面中 点击添加,前端应该发送一个 ajax请求,将 sku_id,count,jwt(可选),selected(可选)


1. 接收数据,对数据进行校验
2. 获取 sku_id,count,selected
3. 获取用户,根据用户信息判断登录状态
4. 登录用户redis
    4.1 连接redis
    4.2 保存数据到redis中
    4.3 返回响应
5. 未登录cookie
    5.1 先读取cookie数据,判断cookie中是否有购物车信息   str --> base64 -->pickle -->dict
        如果有则需要读取
        没有则不用管

    5.2 更新购物车信息  dict

    5.3 需要对购物车信息进行处理 dict --> pickle --> base64 -->str

    5.4 返回响应

POST            /cart/


"""
from .serializers import CartSerializer, CartSKUSerializer
from rest_framework.views import APIView
class CartView(APIView):

    # 如果传递的token有问题,就不能实现 购物车功能
    # 虽然token有问题,但是我们还是应该让用户 把购物车的功能实现了,等到 下单的时候 必须让它登录
    # 我们先不进行 用户的身份的验证 重写 视图的 perform_authentication 方法就可以
    # 等需要使用的时候再验证
    def perform_authentication(self, request):
        pass


    def post(self,request):

        # 1. 接收数据,对数据进行校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 2. 获取 sku_id,count,selected
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')
        # 3. 获取用户,根据用户信息判断登录状态
        try:
            user = request.user
        except Exception as e:
            user = None

        # 我们要判断用户的登录状态
        # is_authenticated 认证用户为True
        # 没有认证为 False
        # request.user.is_authenticated

        if user is not None and user.is_authenticated:

            # 4. 登录用户redis
            #     4.1 连接redis
            redis_conn = get_redis_connection('cart')
            #     4.2 保存数据到redis中

            # hash
            # redis_conn.hset('cart_%s' % user.id, sku_id, count)
            redis_conn.hincrby('cart_%s' % user.id, sku_id, count)

            # set
            # redis_conn.sadd('cart_selected_%s' % user.id, sku_id)
            # #     4.3 返回响应
            # return Response(serializer.data)
            pl = redis_conn.pipeline()
            # hash
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            # set
            pl.sadd('cart_selected_%s' % user.id, sku_id)

            # 让管道执行指令
            pl.execute()


        else:

            # 5. 未登录

            #     5.1 先读取cookie数据,判断cookie中是否有购物车信息   str --> base64 -->pickle -->dict
            #         如果有则需要读取
            #         没有则不用管
            cookie_str = request.COOKIES.get('cart')
            if cookie_str is not None:
                # 说明有值
                # 经过base64解码,变成二进制
                decode = base64.b64decode(cookie_str)
                # 再经过 pickle.loads 操作转换为 字典
                cart = pickle.loads(decode)
                # cart = {1:{count:5,selected:True}}
            else:
                cart={}

            #     5.2 更新购物车信息  dict
            # 先判断 skU_id 是否存在 在 cart中
            # 如果存在 则进行个数的累加
            # 如果不存在,直接写入
            if sku_id in cart:
                # 存在
                # 获取原来的个数
                #cart = {1:{count:5,selected:True}}
                orginal_count = cart[sku_id]['count']
                # 累加
                count += orginal_count

            # cart = {1:{count:5,selected:True}}
            # {count:5,selected:True}
            cart[sku_id] = {
                'count': count,
                'selected': selected
            }

            #     5.3 需要对购物车信息进行处理 dict --> pickle --> base64 -->str
            # cart = {1:{count:5,selected:True}}
            # 先进行 pickle.dumps 转换为二进制
            dumps = pickle.dumps(cart)
            # 再经过base64的编码
            encode = base64.b64encode(dumps)
            # 因为 经过base64的编码 之后,返回的还是 bytes类型,所以转换给字符串
            new_cookie = encode.decode()

            response = Response(serializer.data)

            response.set_cookie('cart',new_cookie)

            #     5.4 返回响应
            return response

    """

    用户点击购物车列表, 应该让前端传递一个 jwt token (前端是在headers中填写)


    1. 需要获取用户信息
    2. 根据用户信息进行判断,
    3.登录用户redis
        3.1 连接redis
        3.2 获取数据
            hash: sku_id:count
            set:  sku_id
    4.未登录用户cookie
        4.1 先读取cookie数据
        4.2 判断是否存储cookie数据  存在就转换数据,不存在就初始化购物车
            cart={sku_id:{count:xxx,selected:True}}
        4.3 获取商品的id

    5 根据id查询商品的详细信息 [SKU,SKU]
    6 对对象列表进行序列化操作
    7 返回响应

    GET     /cart/
    """

    def get(self,request):

        # 1. 需要获取用户信息
        try:
            user = request.user
        except Exception:
            user = None

        # 2. 根据用户信息进行判断
        if user is not None and user.is_authenticated:

            # 3.登录用户redis
            #     3.1 连接redis
            redis_conn = get_redis_connection('cart')
            #     3.2 获取数据
            #         hash: sku_id:count
            # {sku_id:count,sku_id:count}
            sku_count = redis_conn.hgetall('cart_%s'%user.id)

            #         set:  sku_id
            #选中的id
            selected_ids = redis_conn.smembers('cart_selected_%s'%user.id)

            #3.3 将redis的数据格式转换为 cart样式的
            ## cart = {sku_id:{count:1,selected:True}}
            cart = {}

            # 对 sku_count 进行遍历
            for sku_id,count in sku_count.items():

                if sku_id in selected_ids:
                    selected=True
                else:
                    selected=False

                cart[int(sku_id)] = {
                    'count':int(count),
                    # 'selected': sku_id in selected_ids
                    'selected': selected
                }

        else:
            # 4.未登录用户cookie
            #     4.1 先读取cookie数据
            cookie_str = request.COOKIES.get('cart')
            #     4.2 判断是否存储cookie数据  存在就转换数据,不存在就初始化购物车
            #         cart={sku_id:{count:xxx,selected:True}}
            if cookie_str is not None:
                # str --> base64.decode() --> pickle.loads
                decode = base64.b64decode(cookie_str)
                cart = pickle.loads(decode)
            else:
                cart = {}
                # cart = {sku_id:{count:1,selected:True}}


        # cart = {1:{count:1,selected:True}}
        ids = cart.keys()
        # [1,2,3]
        # 5 根据id查询商品的详细信息 [SKU,SKU]
        skus = SKU.objects.filter(id__in=ids)

        for sku in skus:
            # # cart = {sku_id:{count:1,selected:True}}
            sku.count= cart[sku.id]['count']
            sku.selected=cart[sku.id]['selected']

        # 6 对对象列表进行序列化操作
        serializer = CartSKUSerializer(skus,many=True)
        # 7 返回响应
        return Response(serializer.data)




    """
    修改购物车的业务逻辑

    用户在修改数据的时候,前端应该将 修改之后的  sku_id,count,selected 发送给后端
    额外强调一下 ,我们的count是用户 最终的值

    1. 接收数据,并进行数据的校验
    2. 获取sku_id,count,selected的值
    3. 获取用户信息,并进行判断
    4. 登录用户redis
        4.1 连接redis
        4.2 更新数据
        4.3 返回数据 (一定要将 最终的商品数据 返回回去)
    5. 未登录用户 cookie
        5.1 读取cookie,并判断数据是否存在
        5.2 更新数据 dict
        5.3 对字典进行处理
        5.4 返回响应(一定要将 最终的商品数据 返回回去)


    """

    def put(self,request):

        #1. 接收数据,并进行数据的校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2. 获取sku_id,count,selected的值
        sku_id = serializer.data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')


        # 3. 获取用户信息,并进行判断
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            # 4. 登录用户redis
            #     4.1 连接redis
            redid_conn = get_redis_connection('cart')
            #     4.2 更新数据
            # hash
            redid_conn.hset('cart_%s' % user.id, sku_id, count)
            # set
            # 选中状态
            if selected:
                #选中
                redid_conn.sadd('cart_selected_%s'%user.id,sku_id)

            else:
                # 未选中
                redid_conn.srem('cart_selected_%s' % user.id, sku_id)

                #     4.3 返回数据 (一定要将 最终的商品数据 返回回去)
                return Response(serializer.data)

        else:
            # 5. 未登录用户 cookie
            #     5.1 读取cookie,并判断数据是否存在
            cookie_str = request.COOKIES.get('cart')

            if cookie_str is not None:
                #读取数据
                cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                cart = {}

            #     5.2 更新数据 dict
            # cart = {1:{count:xxx,seleect:xxxx}}

            if sku_id in cart:
                cart[int(sku_id)] = {
                    'count': int(count),
                    'selected': selected
                }

            #     5.3 对字典进行处理   base64处理结果是二进制所以decode
            new_cookie = base64.b64encode(pickle.dumps(cart)).decode()

            #     5.4 返回响应(一定要将 最终的商品数据 返回回去)
            response = Response(serializer.data)
            response.set_cookie('cart', new_cookie)


            return response

    """
       删除功能
       用户点击删除按钮的时候,前端应该发送一个 ajax请求,请求中包含 sku_id        jwt

       1. 接收参数,并对参数进行校验
       2. 获取用户信息,并根据用户信息进行判断
       3. 登录用户redis
           3.1 连接redis
           3.2 删除数据 hash,set
           3.3 返回响应
       4. 未登录用户 cookie
           4.1 获取cookie数据并进行判断
           4.2 删除数据
           4.3 dict 对购物车数据进行处理
           4.4 返回响应

       """

    def delete(self,request):

        #
        # 1. 接收参数,并对参数进行校验
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.data.get('sku_id')

        # 2. 获取用户信息,并根据用户信息进行判断
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 3. 登录用户redis
            #     3.1 连接redis
            redis_conn = get_redis_connection('cart')
            #     3.2 删除数据 hash,set
            redis_conn.hdel('cart_%s'%user.id,sku_id)

            redis_conn.srem('cart_selected_%s' % user.id, sku_id)

            #     3.3 返回响应
            return Response(serializer.data)
        else:
            # 4. 未登录用户 cookie
            #     4.1 获取cookie数据并进行判断
            cookie_str = request.COOKIES.get('cart')
            if cookie_str is not None:
                cart = pickle.loads(base64.b64decode(cookie_str))

            else:
                cart = {}

            #     4.2 删除数据
            if sku_id in cart:
                del cart[sku_id]
            #     4.3 dict 对购物车数据进行处理
            new_cookie = base64.b64encode(pickle.dumps(cart)).decode()

            #     4.4 返回响应
            response = Response(serializer.data)

            response.set_cookie('cart', new_cookie)

            return response









































